package cn.tannn.ops.backup.controller;


import cn.tannn.jdevelops.annotations.web.mapping.PathRestController;
import cn.tannn.jdevelops.exception.built.BusinessException;
import cn.tannn.jdevelops.jpa.constant.SQLOperator;
import cn.tannn.jdevelops.jpa.result.JpaPageResult;
import cn.tannn.jdevelops.result.response.ResultPageVO;
import cn.tannn.jdevelops.result.response.ResultVO;
import cn.tannn.jdevelops.result.utils.ListTo;
import cn.tannn.ops.backup.constant.DbType;
import cn.tannn.ops.backup.controller.dto.DbBackupAdd;
import cn.tannn.ops.backup.controller.dto.DbBackupEdit;
import cn.tannn.ops.backup.controller.dto.DbBackupPage;
import cn.tannn.ops.backup.entity.DbBackup;
import cn.tannn.ops.backup.model.SshRemoteFile;
import cn.tannn.ops.backup.service.DbBackupService;
import cn.tannn.ops.ssh.entity.SshInfo;
import cn.tannn.ops.ssh.service.SshInfoService;
import cn.tannn.ops.util.SSHUtil;
import com.jcraft.jsch.Session;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static cn.hutool.core.io.FileUtil.getMimeType;

/**
 * 数据库备份
 *
 * @author tnnn
 * @version V1.0
 * @date 2024-9-10
 */
@PathRestController("dbBackup")
@Slf4j
@Tag(name = "数据库备份")
@RequiredArgsConstructor
public class DbBackupController {

    private final DbBackupService dbBackupService;
    private final SshInfoService sshInfoService;


    @GetMapping("/dbTypes")
    @Operation(summary = "数据库类型", description = "具体说明看代码注释")
    public ResultVO<List<Map<String, String>>> dbTypes() {
        List<Map<String, String>> values = DbType.toMap();
        return ResultVO.success(values);
    }

    @GetMapping("/{id}")
    @Operation(summary = "根据ID获取详情", description = "save")
    public ResultVO<DbBackup> info(@PathVariable("id") Long id) {
        DbBackup bean = dbBackupService.findOnly("id", id).orElse(new DbBackup());
        return ResultVO.success(bean);
    }

    @Operation(summary = "新增数据库备份")
    @PostMapping("append")
    public ResultVO<String> append(@RequestBody @Valid DbBackupAdd append) {
        dbBackupService.saveOneByVo(append);
        return ResultVO.success();
    }

    @Operation(summary = "分页查询")
    @PostMapping("page")
    public ResultPageVO<DbBackup, JpaPageResult<DbBackup>> page(@RequestBody @Valid DbBackupPage page) {
        Page<DbBackup> byBean = dbBackupService.findPage(page, page.getPage());
        JpaPageResult<DbBackup> pageResult = JpaPageResult.toPage(byBean);
        return ResultPageVO.success(pageResult, "查询成功");
    }

    @Operation(summary = "集合")
    @GetMapping("lists")
    public ResultVO<List<DbBackup>> lists() {
        List<DbBackup> finds = dbBackupService.finds();
        return ResultVO.success(ListTo.to(DbBackup.class, finds));
    }


    @Operation(summary = "删除数据库备份")
    @PostMapping("delete")
    @Parameter(name = "ids", description = "id集合", required = true)
    public ResultVO<String> delete(@RequestBody List<Long> ids) {
        dbBackupService.delete("id", SQLOperator.IN, ids);
        return ResultVO.success();
    }

    @Operation(summary = "编辑数据库备份")
    @PostMapping("edit")
    public ResultVO<String> edit(@RequestBody @Valid DbBackupEdit edit) {
        dbBackupService.update(edit, SQLOperator.EQ);
        return ResultVO.success();
    }

    @GetMapping("/backup")
    @Operation(summary = "备份")
    public ResultVO<String> backup(@RequestParam("id") Long id) {
        dbBackupService.findOnly("id", id).ifPresent(exist -> {
            if (exist.getSshId() == null) {
                throw new BusinessException("请设置ssh后在进行备份");
            }
            sshInfoService.findOnly("id", exist.getSshId()).ifPresent(ssh -> {
                dbBackupService.backup(exist, ssh);
            });
        });
        return ResultVO.successMessage("备份成功,请去备份存储地址查看[按时间排序],如何文件还没生成请等待生成后再看");
    }


    @GetMapping("/files")
    @Operation(summary = "查询备份的文件")
    public ResultVO<List<SshRemoteFile>> findBackupFiles(@RequestParam("id") Long id) {
        Optional<DbBackup> only = dbBackupService.findOnly("id", id);
        if (only.isPresent()) {
            DbBackup dbBackup = only.get();
            if (dbBackup.getSshId() == null) {
                return ResultVO.failMessage("ssh未设置");
            }
            Optional<SshInfo> ssh = sshInfoService.findOnly("id", dbBackup.getSshId());
            return ssh.map(sshInfo -> ResultVO.success(dbBackupService.findBackupFiles(dbBackup, sshInfo)))
                    .orElseGet(() -> ResultVO.failMessage("ssh不存在请重新设置ssh"));
        } else {
            return ResultVO.failMessage("请刷新列表后再试");
        }
    }

    @GetMapping("/files/delete")
    @Operation(summary = "删除备份的文件")
    @Parameter(name = "fileName", description = "文件名注意是文件名")
    @Parameter(name = "id", description = "备份数据库id")
    public ResultVO<String> deleteBackup(@RequestParam("id") Long id
            , @RequestParam("fileName") String fileName) {
        dbBackupService.findOnly("id", id).ifPresent(dbBackup -> {
            if (dbBackup.getSshId() == null) {
                throw new BusinessException("当前数据库未ssh设置");
            }
            sshInfoService.findOnly("id", dbBackup.getSshId()).ifPresent(ssh -> {
                try {
                    Session session = SSHUtil.connectionServer(ssh.getUsername()
                            , ssh.getPassword()
                            , ssh.getUrl()
                            , ssh.getPort());
                    String rfile = getRfile(fileName, dbBackup);
                    SSHUtil.remoteRemove(session, rfile);
                } catch (Exception e) {
                    log.error("delete backup file {}  error", fileName, e);
                }
            });
        });
        return ResultVO.successMessage("删除成功");
    }

    @GetMapping("/files/download")
    @Operation(summary = "下载备份的文件")
    @Parameter(name = "fileName", description = "文件名注意是文件名")
    @Parameter(name = "id", description = "备份数据库id")
    public void downloadBackup(@RequestParam("id") Long id
            , @RequestParam("fileName") String fileName
            , HttpServletResponse response) {
        Optional<DbBackup> backup = dbBackupService.findOnly("id", id);
        if (backup.isPresent()) {
            DbBackup dbBackup = backup.get();
            Optional<SshInfo> sshInfo = sshInfoService.findOnly("id", dbBackup.getSshId());
            if (sshInfo.isPresent()) {
                SshInfo ssh = sshInfo.get();
                Session session = null;
                InputStream inputStream = null;
                try {
                    session = SSHUtil.connectionServer(ssh.getUsername()
                            , ssh.getPassword()
                            , ssh.getUrl()
                            , ssh.getPort());
                    String rfile = getRfile(fileName, dbBackup);
                    inputStream = SSHUtil.remoteDownload(session, rfile, false);

                    response.reset();//避免空行
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType(getMimeType(fileName));
                    // 这里URLEncoder.encode可以防止中文乱码
                    response.setHeader("Content-disposition", "attachment;filename=" + fileName);

                    IOUtils.copy(inputStream, response.getOutputStream());

                } catch (Exception e) {
                    log.error("下载远程文件失败", e);
                    throw new BusinessException("下载失败", e);
                } finally {
                    SSHUtil.disconnectServer(session);
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            throw new BusinessException("下载失败", e);
                        }
                    }
                }

            }
        }
    }

    private static @NotNull String getRfile(String fileName, DbBackup dbBackup) {
        String rfile;
        if (dbBackup.getRemoteBackupPath().endsWith("/")) {
            rfile = dbBackup.getRemoteBackupPath() + fileName;
        } else {
            rfile = dbBackup.getRemoteBackupPath() + "/" + fileName;
        }
        return rfile;
    }
}
